package rlm;

import java.awt.Component;
import java.awt.Container;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import javax.swing.JComponent;

import linearProgramming.Constraint;
import linearProgramming.LinearProgrammingException;
import rlm.constraints.Directions;
import rlm.helper.ComponentNamer;
import rlm.rlcontainer.CompoundPlaceHolder;
import rlm.rlcontainer.PlaceHolder;
import rlm.util.StringUtils;
import alm.ALMException;
import alm.ALMLayout;
import alm.Area;
import alm.Column;
import alm.Row;

public class ALMExtended extends ALMLayout{

	private List<Component> components;
	private List<PlaceHolder> places;
	
	private Map<String, PlaceHolder> placesMap;
	private Map<Component, PlaceHolder> compToPlace;
	private ComponentNamer namer;
	private CompoundPlaceHolder rootPlaceHolder;

	public ALMExtended() throws ALMException {
		super();
		
		components = new ArrayList<Component>();
		places = new ArrayList<PlaceHolder>();
		placesMap = new TreeMap<String, PlaceHolder>();
		compToPlace = new HashMap<Component, PlaceHolder>(); 
	
		rootPlaceHolder = new CompoundPlaceHolder("root");
		
		namer = new ComponentNamer();
	}

	
	public String createName(Object o) {
		return namer.createName(o);
	}


	public List<PlaceHolder> getPlaces() {
		return places;
	}

	/**
	 * adds places and keeps hierarchy
	 * @param p
	 */
	public void addPlace(PlaceHolder p) {
		if (p == rootPlaceHolder)
			return;
		
		if (!getPlaces().contains(p))
			getPlaces().add(p);

		rootPlaceHolder.addChild(p);
	}

	public PlaceHolder getPlace(String name) {
		for (PlaceHolder place : getPlaces()) {
			if (place.getName().equals(name))
				return place;
		}
		return null;
	}

	public CompoundPlaceHolder getRoot() {
		return rootPlaceHolder;
	}


	@Override
	public void addLayoutComponent(String name, Component comp) {
		if (name == null){
			name = comp.getName();
			if (StringUtils.isEmpty(name))
				name = namer.createName(comp);
		}
		
		this.components.add(comp);
		PlaceHolder place = new PlaceHolder((JComponent)comp); 
		compToPlace.put(comp, place);	
		
		addPlace(place);
		
		super.addLayoutComponent(name, comp);
	}

	public List<Component> getComponents() {
		return components;
	}


	@Override
	public void layoutContainer(Container parent) {
		calculateOuterBox();

		// create area is not enough, areas are composite, and some should be created and some not
		
		for (PlaceHolder place : getPlaces()) {
			place.createArea(getLayoutSpec());
		}
		
		try {
			super.layout(parent);
		} catch (LinearProgrammingException e) {
			e.printStackTrace();
		}
		log();
		
	}
	
	private void log() {
		if (1 != 2)
			return;
		
		Set<Constraint> constraints = new HashSet(getLayoutSpec().getConstraints());
		
		System.out.println(" ============== alm extended log =========================");
		for (PlaceHolder place : getPlaces()) {
			int left = place.getComponent().getX();
			int right = left + place.getComponent().getWidth();
			int bottom = place.getComponent().getY();
			int top = bottom + place.getComponent().getHeight();
			Area area = getLayoutSpec().areaOf(place.getComponent());
			//System.out.println(area);
//			System.out.println(place);
			//System.out.println(place + "; ["+left+", "+top +", "+right +", " + bottom+"] "+place.getComponent().getPreferredSize());
			System.out.println(place+ "; ["+left+", "+top +", "+right +", " + bottom+"] " + area);
			area.printConstraints();
			constraints.removeAll(area.getConstraints());
		}
		
		for(Constraint con : constraints){
			System.out.println(con);
		}
		
		System.out.println(" ========================== done =========================");
	}

	
	/**
	 * For all components in this container, fill in the outer bounding box:
	 * In the following way: add the exterval tabs to all places that have null
	 * tabs and no places bordering them in that direction 
	 */
	protected void calculateOuterBox3(){
		// set new tabs to all components
		rootPlaceHolder.getOuterBounds().testAndSetAll(getLayoutSpec());
		rootPlaceHolder.calculateOuterBox();
	}

	
	protected void calculateOuterBox(){
		rootPlaceHolder.getOuterBounds().setDefaultBounds(getLayoutSpec());
		rootPlaceHolder.calculateOuterBox();				
	}
	
	protected void caluclateOuterBox1(){		
		for(PlaceHolder p : places)
			for(Directions dir : Directions.values())
				if (p.getOuterBounds().getTab(dir) == null && p.getPlaces(dir).size() == 0)
					p.getOuterBounds().setTab(dir, dir.getOuterTab(getLayoutSpec()));					
	}
		
	protected void invalidate(){
		// invalidate places
		for(PlaceHolder place : getPlaces()){
			place.clear();
		}
			
		// there should be no areas at this point, but in case there are areas without placeholders
		for (Area area : getLayoutSpec().getAreas()) {
			try {
				area.remove();
			} catch (LinearProgrammingException e) {
				e.printStackTrace();
			}
		}


		ArrayList<Constraint> list = new ArrayList<Constraint>();
		for(Constraint con : getLayoutSpec().getConstraints()){
			list.add(con);
		}
		
		// remove remaining constraints
		for (Constraint con : list){
			try {
				con.remove();
			} catch (LinearProgrammingException e) {
				e.printStackTrace();
			}
		}
		
		getLayoutSpec().getAreas().clear();
//		getLayoutSpec().getVariables().clear();
		getLayoutSpec().getConstraints().clear();
		
		getLayoutSpec().removePresolved();	
	}
	

	public PlaceHolder placeForComp(Component comp){
		return compToPlace.get(comp);
	}

	
	/**
	 * used to init underlying alm data
	 * 
	 */
	public void init(){
		super.getLayoutSpec().init();
		
		rootPlaceHolder = new CompoundPlaceHolder();
	}

	public void init(List<Component> components){
		init();
		addLayoutComponents(components);
	}
	
	private void addLayoutComponents(List<Component> components) {
		for (Component component : components) {
			addLayoutComponent(component.getName(), component);
		}
	}


	/**
	 * used to destroy all underlying alm data (variables, rows, predefined layouts etc.)
	 */
	public void destroy(){
		super.getLayoutSpec().destroy();
		
		components.clear();
		places.clear();
		placesMap.clear();
		compToPlace.clear();
		
		// TODO:: how to clear root element? should it be 
		rootPlaceHolder = null; // new CompoundPlaceHolder();
	}

	/**
	 * clears all place holders, all areas and variables 
	 */
	@Deprecated
	public void clearRef() {
		for(Area area : this.getAreas()){
			try {
				area.remove();
			} catch (LinearProgrammingException e) {
				System.out.println("clear: error removing area :" + area);
			}
		}
		
		for(Column column : this.getColumns()){
			try {
				column.remove();
			} catch (LinearProgrammingException e) {
				System.out.println("clear: error removing column :" + column);
			}
		}
		
		for(Column column : this.getColumns()){
			try {
				column.remove();
			} catch (LinearProgrammingException e) {
				System.out.println("clear: error removing column :" + column);
			}
		}
		
		for(Row row : this.getRows()){
			try {
				row.remove();
			} catch (LinearProgrammingException e) {
				System.out.println("clear: error removing row :" + row);
			}
		}
		
		this.getPlaces().clear();
		this.getAreas().clear();
		this.getColumns().clear();
		this.getRows().clear();
		
		for(Constraint c : this.getLayoutSpec().getConstraints()){
			try {
				c.remove();
			} catch (LinearProgrammingException e) {
				System.out.println("clear: error removing constraint: " + c);
			}
		}
		
		this.getLayoutSpec().getConstraints().clear();
		this.getLayoutSpec().getObjFunctionSummands().clear();
		
//		for(Variable var : this.getLayoutSpec().getVariables()){
//			try {
//				var.remove();
//			} catch (LinearProgrammingException e) {
//				System.out.println("clear: error removing variable: " + var);
//			}
//		}
		this.getLayoutSpec().getVariables().clear();
		
		
	}
	
}
